﻿// <copyright file="PermissionCopyForm.cs" company="Sinclair Community College">
// Copyright (c) Sinclair Community College. All rights reserved.
// </copyright>

namespace SinclairCC.AppVSuite.PermissionCopy
{
    using System;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using SinclairCC.AppVSuite.Shared;

    /// <summary>
    /// This class represents a form that lets the user select the source and destination application or
    /// application group for the permission copy process.
    /// </summary>
    internal partial class PermissionCopyForm : Form
    {
        /// <summary>
        /// A connection to the App-V database.
        /// </summary>
        private System.Data.SqlClient.SqlConnection connAppV;

        /// <summary>
        /// Initializes a new instance of the PermissionCopyForm class.
        /// </summary>
        public PermissionCopyForm()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Computes a cryptographic has for a string.
        /// </summary>
        /// <param name="text">
        /// The string whose hash is to be computed.
        /// </param>
        /// <returns>
        /// Returns a string containing the hash for the input string.
        /// </returns>
        public static string ComputeHash(string text)
        {
            System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
            byte[] hashArray = md5.ComputeHash(System.Text.Encoding.Unicode.GetBytes(text));
            md5.Clear();

            // Convert the byte array to a string. Remove the dashes. Convert the string to lower case.
            return System.BitConverter.ToString(hashArray).Replace("-", null).ToLower(System.Globalization.CultureInfo.CurrentCulture);
        }

        /// <summary>
        /// Handles the Click event for the Copy button.
        /// </summary>
        /// <param name="sender">
        /// The button being clicked.
        /// </param>
        /// <param name="e">
        /// Data specific to this event.
        /// </param>
        private void ClickCopyButton(object sender, EventArgs e)
        {
            if ((this.toTree.SelectedNode == null) || (this.fromTree.SelectedNode == null))
            {
                MessageBox.Show(this, "You must select a source and destination application or application group.", "App-V Permission Copy", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, 0);
                return;
            }

            TreeNodeTag fromNodeTag = (TreeNodeTag)this.fromTree.SelectedNode.Tag;
            PermissionListEditor permissionEditor = new PermissionListEditor(fromNodeTag);
            if ((permissionEditor.ShowDialog(this) == DialogResult.OK) && (permissionEditor.SelectedGroups != null))
            {
                TreeNodeTag toNodeTag = (TreeNodeTag)this.toTree.SelectedNode.Tag;
                if (permissionEditor.ReplaceExistingGroups)
                {
                    AppVDatabaseFunctions.DeleteAllAccessToApps(toNodeTag.Type, toNodeTag.ID);
                }

                AppVDatabaseFunctions.AddAccessToApp(permissionEditor.SelectedGroups, toNodeTag.Type, toNodeTag.ID);
                MessageBox.Show(this, "Access permissions have been copied successfully.", "App-V Permission Copy", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, 0);
            }
        }

        /// <summary>
        /// Handles the Closed event for the form.
        /// </summary>
        /// <param name="sender">
        /// The form that has been closed.
        /// </param>
        /// <param name="e">
        /// Data specific to this event.
        /// </param>
        private void FormClosedEvent(object sender, FormClosedEventArgs e)
        {
            if (this.connAppV != null)
            {
                if (this.connAppV.State == ConnectionState.Open)
                {
                    this.connAppV.Close();
                }

                this.connAppV.Dispose();
            }
        }

        /// <summary>
        /// Initializes the "to" and "from" trees with a list of the root application groups in the App-V database.
        /// </summary>
        private void InitializeTrees()
        {
            this.toTree.BeginUpdate();
            this.fromTree.BeginUpdate();
            this.toTree.Nodes.Clear();
            this.fromTree.Nodes.Clear();

            DataTable appGroups = AppVDatabaseFunctions.GetApplicationGroups(this.connAppV, null);
            foreach (DataRow row in appGroups.Rows)
            {
                string groupName = (string)row["name"];
                TreeNode newNode = new TreeNode(groupName);
                newNode.Tag = new TreeNodeTag((int)row["app_group_id"], AppVObjectType.ApplicationGroup);
                newNode.ImageKey = "appgroup";
                newNode.Nodes.Add("_dummy");

                TreeNode newNodeCopy = (TreeNode)newNode.Clone();

                this.toTree.Nodes.Add(newNode);
                this.fromTree.Nodes.Add(newNodeCopy);
            }

            DataTable apps = AppVDatabaseFunctions.GetApplications(this.connAppV, null, false);
            foreach (DataRow row in apps.Rows)
            {
                System.Text.StringBuilder nodeText = new StringBuilder();
                nodeText.Append((string)row["name"]);
                nodeText.Append(' ');
                nodeText.Append((string)row["version"]);

                string iconFilePath = (string)row["icon_file"];
                iconFilePath = System.Environment.ExpandEnvironmentVariables(iconFilePath);

                TreeNode newNode = new TreeNode(nodeText.ToString());
                newNode.Tag = new TreeNodeTag((int)row["app_id"], AppVObjectType.Application);
                if (System.IO.File.Exists(iconFilePath))
                {
                    string hash = ComputeHash(iconFilePath);
                    try
                    {
                        Icon ico = new Icon(iconFilePath);
                        this.icons.Images.Add(hash, ico);
                        newNode.ImageKey = hash;
                        newNode.SelectedImageKey = hash;
                    }
                    catch (ArgumentException)
                    {
                    }
                }

                TreeNode newNodeCopy = (TreeNode)newNode.Clone();

                this.toTree.Nodes.Add(newNode);
                this.fromTree.Nodes.Add(newNodeCopy);
            }

            this.toTree.EndUpdate();
            this.fromTree.EndUpdate();
        }

        /// <summary>
        /// Handles the BeforeExpand event for the two trees.
        /// </summary>
        /// <param name="sender">
        /// The TreeView that is having one of its nodes expanded.
        /// </param>
        /// <param name="e">
        /// Data specific to this event.
        /// </param>
        private void BeforeTreeNodeExpand(object sender, TreeViewCancelEventArgs e)
        {
            e.Node.TreeView.BeginUpdate();

            TreeNodeTag nodeTag = (TreeNodeTag)e.Node.Tag;
            e.Node.Nodes.Clear();

            // Add the child application groups of the application group being expanded.
            DataTable appGroups = AppVDatabaseFunctions.GetApplicationGroups(this.connAppV, nodeTag.ID);
            foreach (DataRow row in appGroups.Rows)
            {
                TreeNode newNode = new TreeNode((string)row["name"]);
                newNode.Tag = new TreeNodeTag((int)row["app_group_id"], AppVObjectType.ApplicationGroup);
                newNode.Nodes.Add("_dummy");
                newNode.ImageKey = "appgroup";
                e.Node.Nodes.Add(newNode);
            }

            // Add the applications contained in the application group being expanded.
            DataTable apps = AppVDatabaseFunctions.GetApplications(this.connAppV, nodeTag.ID, false);
            foreach (DataRow row in apps.Rows)
            {
                System.Text.StringBuilder nodeText = new StringBuilder();
                nodeText.Append((string)row["name"]);
                nodeText.Append(' ');
                nodeText.Append((string)row["version"]);

                string iconFilePath = (string)row["icon_file"];
                iconFilePath = System.Environment.ExpandEnvironmentVariables(iconFilePath);

                TreeNode newNode = new TreeNode(nodeText.ToString());
                newNode.Tag = new TreeNodeTag((int)row["app_id"], AppVObjectType.Application);
                if (System.IO.File.Exists(iconFilePath))
                {
                    string hash = ComputeHash(iconFilePath);
                    try
                    {
                        Icon ico = new Icon(iconFilePath);
                        this.icons.Images.Add(hash, ico);
                        newNode.ImageKey = hash;
                        newNode.SelectedImageKey = hash;
                    }
                    catch (ArgumentException)
                    {
                    }
                }

                e.Node.Nodes.Add(newNode);
            }

            e.Node.TreeView.EndUpdate();
        }

        /// <summary>
        /// Handles the ClientSizeChanged event for the form.
        /// </summary>
        /// <param name="sender">
        /// The form whose client size has changed.
        /// </param>
        /// <param name="e">
        /// Data specific to this event.
        /// </param>
        private void FormClientSizeChanged(object sender, EventArgs e)
        {
            this.copyButton.Left = (this.ClientRectangle.Width - this.copyButton.Width - this.copyButton.Padding.Horizontal) / 2;
        }

        /// <summary>
        /// Handles the Load event for the form.
        /// </summary>
        /// <param name="sender">
        /// The form being loaded.
        /// </param>
        /// <param name="e">
        /// Data specific to the event.
        /// </param>
        private void FormLoadEvent(object sender, EventArgs e)
        {
            this.EnsureConnectionStringExists();
            this.InitializeTrees();
        }

        /// <summary>
        /// Ensures the application has the information it needs to connect to the App-V database.
        /// </summary>
        private void EnsureConnectionStringExists()
        {
            // Read the connection string from the registry, prompting the user for any missing information.
            DialogResult connectionPropertiesResult = System.Windows.Forms.DialogResult.None;
            string connectionString = null;
            do
            {
                try
                {
                    connectionString = Settings.ConnectionString;
                }
                catch (System.ArgumentNullException)
                {
                    connectionString = null;
                }

                if (connectionString == null)
                {
                    connectionPropertiesResult = new ConnectionProperties().ShowDialog(this);
                }
            }
            while ((connectionString == null) && (connectionPropertiesResult != System.Windows.Forms.DialogResult.Cancel));

            // If the connection string is successfully obtained from the registry, connect to the
            // App-V database and initialize the form's ListView control. Otherwise, close the form.
            if (connectionString != null)
            {
                this.connAppV = new System.Data.SqlClient.SqlConnection(connectionString);
                try
                {
                    this.connAppV.Open();
                }
                catch (System.Data.SqlClient.SqlException sqlError)
                {
                    StringBuilder errorMessage = new StringBuilder("An SQL error occurred while opening a connection to the database.");
                    if (sqlError.Message.Length > 0)
                    {
                        errorMessage.Append(System.Environment.NewLine);
                        errorMessage.Append(System.Environment.NewLine);
                        errorMessage.Append(sqlError.Message);
                    }

                    MessageBox.Show(this, errorMessage.ToString(), "App-V Permission Copy", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0);
                }
                catch (System.InvalidOperationException invalidOpError)
                {
                    StringBuilder errorMessage = new StringBuilder("An invalid operation error occurred while opening a connection to the database.");
                    if (invalidOpError.Message.Length > 0)
                    {
                        errorMessage.Append(System.Environment.NewLine);
                        errorMessage.Append(System.Environment.NewLine);
                        errorMessage.Append(invalidOpError.Message);
                    }

                    MessageBox.Show(this, errorMessage.ToString(), "App-V Permission Copy", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0);
                }
            }
            else
            {
                this.Close();
            }
        }

        /// <summary>
        /// Handles the Click event for the Configure Connection button.
        /// </summary>
        /// <param name="sender">
        /// The button being clicked.
        /// </param>
        /// <param name="e">
        /// Data specific to this event.
        /// </param>
        private void ClickConfigureConnectionButton(object sender, EventArgs e)
        {
            DialogResult result = new ConnectionProperties().ShowDialog(this);
            if (result == System.Windows.Forms.DialogResult.OK)
            {
                this.EnsureConnectionStringExists();
                this.InitializeTrees();
            }
        }
    }
}
